package de.coe.hendricjabs.friendmap.server;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.SocketException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.io.IOUtils;

import sun.awt.image.DataBufferNative;
import de.coe.hendricjabs.friendmap.Contact;
import de.coe.hendricjabs.friendmap.ConversationTypes;
import de.coe.hendricjabs.friendmap.server.exceptions.SystemException;
import de.coe.hendricjabs.friendmap.server.exceptions.SystemException.ErrorType;

public class WorkerRunnable implements Runnable {

    protected Socket clientSocket = null;
    protected String serverText = null;
    private BufferedReader input = null;
    private BufferedWriter output = null;

    private final char EOF = (char) 26;

    public WorkerRunnable(Socket clientSocket, String serverText) {
        this.clientSocket = clientSocket;
        try {
            this.clientSocket.setSoTimeout(20000);
        } catch (SocketException e) {
            e.printStackTrace();
        }
        this.serverText = serverText;
    }

    public void run() {
        try {
            System.out.println("[Server] Verbunden mit " + clientSocket.getLocalSocketAddress() + "...");

            input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            output = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));

            System.out.println("[Server] schalte auf Empfang");
            List<String> lines = new ArrayList<String>();

            String line;
            while ((line = input.readLine()) != null) {
                if (line != null && !line.equals("")) {
                    lines.add(line);
                    break;
                }
            }

            if (lines.size() > 0)
                System.out.println("[Server] Empfangen...");
            else
                System.out.println("[Server] Keine Daten Empfangen...");
            StringBuilder sb = new StringBuilder();
            for (String s : lines) {
                System.out.println("[REQUEST]:" + s);
                sb.append(s);
            }

            // Request empfangen und verarbeiten
            Request request = processRequest(sb.toString());
            if (request == null)
                throw new SystemException(ErrorType.INVALID_CONVERSATION_FORMAT);
            // Response senden
            sendResponse(request);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(input);
            IOUtils.closeQuietly(output);
            IOUtils.closeQuietly(clientSocket);
        }
    }

    private Request processRequest(String s) {

        // Empfangen des Requests
        try {
            Request request = Request.fromJSON(s);
            System.out.println("[SERVER]  Anfrage Emfangen!");
            return request;
        } catch (SystemException e) {
            System.err.println("[SERVER] Verbindungsabbruch: " + e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private void sendResponse(Request request) {
        System.out.println("[Server] Sende....");
        Response response = null;
        ArrayList<Contact> c;

        switch (request.getType()) {
        case ConversationTypes.CT_BROADCAST:
            // FIXME: Implementieren der Responseersteller
            c = getContactListFromDbByMobile(request.getMobile());
            response = new BroadcastResponse(c);
            break;
        case ConversationTypes.CT_SINGLE:
            // Singlerequest ist eine Registrierung
            SingleRequest sreq = processSingleRequest();
            Contact contact = new Contact(sreq.getMobile(), String.valueOf(new Date().getTime()), null, null);
            SingleResponse sres = new SingleResponse();
            sres.setError(Boolean.toString(!addContactToDB(contact)));
            response = sres;
            break;
        case ConversationTypes.CT_COORDINATES:
            CoordinateRequest creq = processCoordinateRequest();
            CoordinateResponse cres = new CoordinateResponse(creq.getMobile());
            response = cres;
            break;
        case ConversationTypes.CT_CONTACT:
            ContactRequest req = processContactRequest();
            ContactResponse res = new ContactResponse(req.getMobile());
            Contact con = new Contact();
            con.setCoordinates(req.getCoordinates());
            con.setMobilfunknummer(req.getMobile());

            res.setSuccess(addContactToDB(con));
            response = res;
            break;
        }

        if (response == null) {
            throw new SystemException(ErrorType.INVALID_CONVERSATION_FORMAT);
        } else {
            // Sende JSON als String
            try {
                System.out.println("[RESPONSE-SERVER] " + response.toJSON());
                output.write(response.toJSON() + EOF);
                output.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    private SingleRequest processSingleRequest() {
        StringBuilder sb = new StringBuilder();
        String line;
        try {
            while ((line = input.readLine()) != null) {
                if (line != null && !line.equals("")) {
                    sb.append(line);
                    break;
                }
            }
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        // Empfangen des ContactRequests
        try {
            SingleRequest request = (SingleRequest) SingleRequest.fromJSON(sb.toString());
            System.out.println("[SERVER]  Anfrage Emfangen!");
            System.out.println("[REQUEST] " + sb.toString());
            return request;
        } catch (SystemException e) {
            System.err.println("[SERVER] Verbindungsabbruch: " + e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    private ContactRequest processContactRequest() {
        StringBuilder sb = new StringBuilder();
        String line;
        try {
            while ((line = input.readLine()) != null) {
                if (line != null && !line.equals("")) {
                    sb.append(line);
                    break;
                }
            }
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        // Empfangen des ContactRequests
        try {
            ContactRequest request = (ContactRequest) ContactRequest.fromJSON(sb.toString());
            if (request.getMobile() != null) {
                addContactsToContactlist(request.getContacts(), request.getMobile());

                System.out.println("[SERVER]  Anfrage Emfangen!");
                System.out.println("[REQUEST] " + sb.toString());
            }
            return request;
        } catch (SystemException e) {
            System.err.println("[SERVER] Verbindungsabbruch: " + e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;

    }

    private CoordinateRequest processCoordinateRequest() {
        StringBuilder sb = new StringBuilder();
        String line;
        try {
            while ((line = input.readLine()) != null) {
                if (line != null && !line.equals("")) {
                    sb.append(line);
                    break;
                }
            }
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        // Empfangen des CoordinateRequests
        try {
            CoordinateRequest request = (CoordinateRequest) CoordinateRequest.fromJSON(sb.toString());
            setDatabaseCoordinates(request.getMobile(), request.getCoordinates());
            System.out.println("[SERVER]  Anfrage Emfangen!");
            System.out.println("[REQUEST] " + sb.toString());
            return request;
        } catch (SystemException e) {
            System.err.println("[SERVER] Verbindungsabbruch: " + e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;

    }

    private void setDatabaseCoordinates(String mobile, HashMap<String, String> coordinates) {
        // TODO: Datenbankverbindung usw
        ArrayList<Contact> c = new ArrayList<Contact>();

        DBController dbc = null;
        try {
            dbc = DBController.getInstance();
            Connection connection = dbc.getConnection();
            if (!connection.isClosed()) {
                PreparedStatement ps = connection.prepareStatement("UPDATE " + DBController.TAB_CONTACT + " SET "
                        + DBController.COL_LATITUDE + "=?, " + DBController.COL_LONGITUDE + "=? " + "WHERE "
                        + DBController.COL_MOBILE + "=?;");

                ps.setString(1, coordinates.get("latitude"));
                ps.setString(2, coordinates.get("longitude"));
                ps.setString(3, mobile);
                ps.execute();

            } else {
                System.err.println("Datenbank ist geschlossen!!");
            }
        } catch (SQLException e1) {
            e1.printStackTrace();
        }

    }

    /**
     * Gibt gesuchte Kontakte zur�ck
     * 
     * @param mobile
     * @return
     */
    private ArrayList<Contact> getContactListFromDbByMobile(String mobile) {
        // TODO: Datenbankverbindung usw
        ArrayList<Contact> c = new ArrayList<Contact>();

        DBController dbc = null;
        try {
            dbc = DBController.getInstance();
            Connection connection = dbc.getConnection();
            if (!connection.isClosed()) {
                PreparedStatement ps = connection.prepareStatement("SELECT " + DBController.COL_MOBILE + ", "
                        + DBController.COL_DATE + ", "
                        + DBController.COL_LATITUDE + ", " + DBController.COL_LONGITUDE + " FROM "
                        + DBController.TAB_CONTACTLIST + " LEFT JOIN " + DBController.TAB_CONTACT + " ON "
                        + DBController.COL_MOBILE2 + "=" + DBController.COL_MOBILE + " WHERE "
                        + DBController.COL_MOBILE1 + " = ?;");

                ps.setString(1, mobile);

                ResultSet rs = ps.executeQuery();

                while (rs.next()) {
                    c.add(new Contact(rs.getString(DBController.COL_MOBILE), rs.getString(DBController.COL_DATE), rs
                            .getString(DBController.COL_LATITUDE), rs.getString(DBController.COL_LONGITUDE)));
                }
            } else {
                System.err.println("Datenbank ist geschlossen!!");
            }
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
        for (Contact con : c) {
            System.out.println("[DB]" + con.getMobilfunknummer());
        }
        return c;
    }

    private boolean addContactsToContactlist(ArrayList<Contact> contacts, String mobile) {
        boolean ret = true;
        DBController dbc = null;
        try {
            dbc = DBController.getInstance();
            Connection connection = dbc.getConnection();
            if (!connection.isClosed()) {
                for (Contact c : contacts) {
                    if (dbc.exists(c.getMobilfunknummer())) {
                        // Mobilfunknummer existiert in DB
                        if (dbc.addContactToContactList(mobile, c.getMobilfunknummer()) == false) {
                            ret = false;
                        } else {
                            System.out.println("[DB] Hinzugef�gt: " + c.getMobilfunknummer());
                        }
                    } else {
                        // Mobilfunknummer existiert nicht in DB

                    }
                }
            } else {
                System.err.println("Datenbank ist geschlossen!!");
            }
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
        for (Contact con : contacts) {

        }
        return ret;
    }

    private boolean addContactToDB(Contact contact) {
        boolean ret = true;
        DBController dbc = null;
        try {
            dbc = DBController.getInstance();
            Connection connection = dbc.getConnection();
            if (!connection.isClosed()) {
                if (!dbc.exists(contact.getMobilfunknummer()) && contact.getMobilfunknummer() != null) {
                    // Mobilfunknummer existiert nicht in DB
                    if (dbc.addContactToDb(contact) == false) {
                        ret = false;
                    } else {
                        System.out.println("[DB] Hinzugef�gt: " + contact.getMobilfunknummer());
                    }
                }
            } else {
                System.err.println("Datenbank ist geschlossen!!");
            }
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
        return ret;
    }

}
